package com.hang.ad.search.impl;

import com.alibaba.fastjson.JSON;
import com.hang.ad.index.CommonStatus;
import com.hang.ad.index.DataTable;
import com.hang.ad.index.adunit.AdUnitIndex;
import com.hang.ad.index.adunit.AdUnitObject;
import com.hang.ad.index.creative.CreativeIndex;
import com.hang.ad.index.creative.CreativeObject;
import com.hang.ad.index.creativeUnit.CreativeUnitIndex;
import com.hang.ad.index.district.UnitDistrictIndex;
import com.hang.ad.index.interest.UnitInterestIndex;
import com.hang.ad.index.keyword.UnitKeywordIndex;
import com.hang.ad.search.ISearch;
import com.hang.ad.search.vo.SearchRequest;
import com.hang.ad.search.vo.SearchResponse;
import com.hang.ad.search.vo.feature.DistrictFeature;
import com.hang.ad.search.vo.feature.FeatureRelation;
import com.hang.ad.search.vo.feature.ItFeature;
import com.hang.ad.search.vo.feature.KeywordFeature;
import com.hang.ad.search.vo.media.AdSlot;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Component;

import java.util.*;

@Slf4j
@Component
public class SearchImpl implements ISearch {
    @Override
    public SearchResponse fetchAds(SearchRequest request) {
        List<AdSlot> adSlots = request.getRequestInfo().getAdSlots();
        KeywordFeature keywordFeature = request.getFeatureInfo().getKeywordFeature();
        DistrictFeature districtFeature = request.getFeatureInfo().getDistrictFeature();
        ItFeature itFeature = request.getFeatureInfo().getItFeature();
        FeatureRelation relation = request.getFeatureInfo().getRelation();

        SearchResponse response = new SearchResponse();
        Map<String, List<SearchResponse.Creative>> adSlot2Ads = response.getAdSlot2Ads();

        for (AdSlot adSlot : adSlots) {
            Set<Long> target;
            Set<Long> adunitIdSet = DataTable.of(AdUnitIndex.class).match(adSlot.getPositionType());

            if (relation == FeatureRelation.AND){
                filterKeywordFeature(adunitIdSet, keywordFeature);
                filterDistrictFeature(adunitIdSet, districtFeature);
                filterItTagFeature(adunitIdSet, itFeature);

                target = adunitIdSet;
            }else {
                target = getOrRelationUnitIds(adunitIdSet, keywordFeature, districtFeature, itFeature);
            }

            List<AdUnitObject> unitObjects = DataTable.of(AdUnitIndex.class).fetch(target);
            filterAdUnitAndPlanStatus(unitObjects, CommonStatus.VALID);
            List<Long> adIds = DataTable.of(CreativeUnitIndex.class).selectAds(unitObjects);
            List<CreativeObject> creatives = DataTable.of(CreativeIndex.class).fetch(adIds);

            filterCreativeByAdSlot(creatives, adSlot.getWidth(), adSlot.getHeight(), adSlot.getType());
            adSlot2Ads.put(adSlot.getAdSlotCode(), buildCreativeResponse(creatives));
        }
        log.info("fetch ads:{}-{}", JSON.toJSONString(request), JSON.toJSONString(response));
        return response;
    }

    private Set<Long> getOrRelationUnitIds(Set<Long> adUnitIdSet, KeywordFeature keywordFeature, DistrictFeature districtFeature, ItFeature itFeature){
        if (CollectionUtils.isEmpty(adUnitIdSet)){
            return Collections.emptySet();
        }
        Set<Long> keywordSet = new HashSet<>();
        Set<Long> districtSet = new HashSet<>();
        Set<Long> itSet = new HashSet<>();
        filterKeywordFeature(keywordSet, keywordFeature);
        filterDistrictFeature(districtSet, districtFeature);
        filterItTagFeature(itSet, itFeature);

        return new HashSet<>(CollectionUtils.union(CollectionUtils.union(keywordSet, districtSet), itSet));

    }

    private void filterKeywordFeature(Collection<Long> adUnitIds, KeywordFeature keywordFeature){
        if (CollectionUtils.isEmpty(adUnitIds)){
            return;
        }
        if (CollectionUtils.isNotEmpty(keywordFeature.getKeywords())){
            CollectionUtils.filter(adUnitIds, a ->
                DataTable.of(UnitKeywordIndex.class).match(a, keywordFeature.getKeywords())
            );

        }
    }

    private void filterDistrictFeature(Collection<Long> adUnitIds, DistrictFeature districtFeature){
        if (CollectionUtils.isEmpty(adUnitIds)){
            return;
        }
        if (CollectionUtils.isNotEmpty(districtFeature.getDistricts())){
            CollectionUtils.filter(adUnitIds, a ->
                    DataTable.of(UnitDistrictIndex.class).match(a, districtFeature.getDistricts())
            );
        }
    }

    private void filterItTagFeature(Collection<Long> adUnitIds, ItFeature itFeature){
        if (CollectionUtils.isEmpty(adUnitIds)){
            return;
        }
        if (CollectionUtils.isNotEmpty(itFeature.getIts())){
            CollectionUtils.filter(adUnitIds, a ->
                    DataTable.of(UnitInterestIndex.class).match(a, itFeature.getIts())
            );
        }
    }

    private void filterAdUnitAndPlanStatus(List<AdUnitObject> unitObjects, CommonStatus status){
        if (CollectionUtils.isEmpty(unitObjects)){
            return;
        }
        CollectionUtils.filter(unitObjects, object -> object.getUnitStatus().equals(status.getStatus()) && object.getAdPlanObject().getPlanStatus().equals(status.getStatus()));
    }

    private void filterCreativeByAdSlot(List<CreativeObject> creativeObjects, Integer width, Integer height, List<Integer> type){
        if (CollectionUtils.isEmpty(creativeObjects)){
            return;
        }
        CollectionUtils.filter(creativeObjects, c -> c.getAuditStatus().equals(CommonStatus.VALID.getStatus())
                && c.getWidth().equals(width) && c.getHeight().equals(height) && type.contains( c.getType()));
    }
    private List<SearchResponse.Creative> buildCreativeResponse(List<CreativeObject> creatives){
        if (CollectionUtils.isEmpty(creatives)){
            return Collections.emptyList();
        }
        CreativeObject creativeObject = creatives.get(Math.abs(new Random().nextInt() % creatives.size()));
        return Collections.singletonList(SearchResponse.convert(creativeObject));

    }
}
